Notebook for cell type identity correlations
General Setup
Setup chunk
Setup reticulate
knitr::opts_chunk$set(fig.width = 8)
knitr::opts_knit$set(root.dir = normalizePath(".."))
knitr::opts_knit$get("root.dir")
[1] "/nas/groups/treutlein/USERS/tomasgomes/projects/pallium_evo"
Load libraries
library(reticulate)
knitr::knit_engines$set(python = reticulate::eng_python)
py_available(initialize = FALSE)
[1] FALSE
use_python(Sys.which("python"))
py_config()
python: /home/tpires/bin/miniconda3/bin/python
libpython: /home/tpires/bin/miniconda3/lib/libpython3.8.so
pythonhome: /home/tpires/bin/miniconda3:/home/tpires/bin/miniconda3
version: 3.8.3 (default, May 19 2020, 18:47:26) [GCC 7.3.0]
numpy: /home/tpires/bin/miniconda3/lib/python3.8/site-packages/numpy
numpy_version: 1.20.2
NOTE: Python version was forced by RETICULATE_PYTHON
Load and reformat data
Load Seurat data
library(igraph)
Attaching package: ‘igraph’
The following objects are masked from ‘package:purrr’:
compose, simplify
The following object is masked from ‘package:tidyr’:
crossing
The following object is masked from ‘package:tibble’:
as_data_frame
The following objects are masked from ‘package:dplyr’:
as_data_frame, groups, union
The following objects are masked from ‘package:stats’:
decompose, spectrum
The following object is masked from ‘package:base’:
union
Load axolotl data
axolotl_gaba = readRDS(file = "data/expression/axolotl/pallium_neuronal_GABA_res07_harmony.RDS")
axolotl_glut = readRDS(file = "data/expression/axolotl/pallium_neuronal_Glut_res05_harmony.RDS")
axolotl_neurons = readRDS(file = "data/expression/axolotl/pallium_neuronal_res07_harmony.RDS")
axolotl_all = readRDS(file = "data/expression/axolotl/palliumres07_harmony.RDS")
Merge axolotl annotations together into a single object
```r
lizard_all = readRDS(\data/expression/lizard_all_v3.RDS\)
turtle_all = readRDS(\data/expression/turtle_all_v3.RDS\)
drerio_brain = readRDS(\data/expression/drerio_brain_v3.RDS\)
finches_brain = readRDS(\data/expression/HVC_RA_X.RDS\)
mouse_brain = readRDS(\data/expression/l5_all_seurat.RDS\)
<!-- rnb-source-end -->
<!-- rnb-chunk-end -->
<!-- rnb-text-begin -->
Organise metadata into a list
<!-- rnb-text-end -->
<!-- rnb-chunk-begin -->
<!-- rnb-source-begin eyJkYXRhIjoiYGBgclxuYGBgclxuYXhvbG90bF9nYWJhID0gcmVhZFJEUyhmaWxlID0gXFxkYXRhL2V4cHJlc3Npb24vYXhvbG90bC9wYWxsaXVtX25ldXJvbmFsX0dBQkFfcmVzMDdfaGFybW9ueS5SRFNcXClcbmF4b2xvdGxfZ2x1dCA9IHJlYWRSRFMoZmlsZSA9IFxcZGF0YS9leHByZXNzaW9uL2F4b2xvdGwvcGFsbGl1bV9uZXVyb25hbF9HbHV0X3JlczA1X2hhcm1vbnkuUkRTXFwpXG5heG9sb3RsX25ldXJvbnMgPSByZWFkUkRTKGZpbGUgPSBcXGRhdGEvZXhwcmVzc2lvbi9heG9sb3RsL3BhbGxpdW1fbmV1cm9uYWxfcmVzMDdfaGFybW9ueS5SRFNcXClcbmF4b2xvdGxfYWxsID0gcmVhZFJEUyhmaWxlID0gXFxkYXRhL2V4cHJlc3Npb24vYXhvbG90bC9wYWxsaXVtcmVzMDdfaGFybW9ueS5SRFNcXClcbmBgYFxuYGBgIn0= -->
```r
```r
axolotl_gaba = readRDS(file = \data/expression/axolotl/pallium_neuronal_GABA_res07_harmony.RDS\)
axolotl_glut = readRDS(file = \data/expression/axolotl/pallium_neuronal_Glut_res05_harmony.RDS\)
axolotl_neurons = readRDS(file = \data/expression/axolotl/pallium_neuronal_res07_harmony.RDS\)
axolotl_all = readRDS(file = \data/expression/axolotl/palliumres07_harmony.RDS\)
<!-- rnb-source-end -->
<!-- rnb-chunk-end -->
<!-- rnb-text-begin -->
Load necessary eggNOG data
<!-- rnb-text-end -->
<!-- rnb-chunk-begin -->
<!-- rnb-source-begin eyJkYXRhIjoiYGBgclxuYGBgclxuZ2FiYV9jbCA9IHBhc3RlMChcXEdBQkFfXFwsIGF4b2xvdGxfZ2FiYSRzZXVyYXRfY2x1c3RlcnMpXG5uYW1lcyhnYWJhX2NsKSA9IGNvbG5hbWVzKGF4b2xvdGxfZ2FiYSlcbmdsdXRfY2wgPSBwYXN0ZTAoXFxHbHV0X1xcLCBheG9sb3RsX2dsdXQkc2V1cmF0X2NsdXN0ZXJzKVxubmFtZXMoZ2x1dF9jbCkgPSBjb2xuYW1lcyhheG9sb3RsX2dsdXQpXG5vdGhlciA9IGF4b2xvdGxfYWxsJGNsYXNzaWZpY2F0aW9uXzJbIShjb2xuYW1lcyhheG9sb3RsX2FsbCkgJWluJSBjKG5hbWVzKGdsdXRfY2wpLCBuYW1lcyhnYWJhX2NsKSkpXVxuYWxsX2Fubm90ID0gZGF0YS5mcmFtZShcXGFsbF9hbm5vdFxcID0gYyhvdGhlciwgZ2FiYV9jbCwgZ2x1dF9jbCkpXG5heG9sb3RsX2FsbCA9IEFkZE1ldGFEYXRhKGF4b2xvdGxfYWxsLCBtZXRhZGF0YSA9IGFsbF9hbm5vdClcbmF4b2xvdGxfYWxsX2ZpbHQgPSBheG9sb3RsX2FsbFssIWlzLm5hKGF4b2xvdGxfYWxsJGFsbF9hbm5vdCkgJiAhZ3JlcGwoXFxuZXVyb25cXCwgYXhvbG90bF9hbGwkYWxsX2Fubm90KV1cbmBgYFxuYGBgIn0= -->
```r
```r
gaba_cl = paste0(\GABA_\, axolotl_gaba$seurat_clusters)
names(gaba_cl) = colnames(axolotl_gaba)
glut_cl = paste0(\Glut_\, axolotl_glut$seurat_clusters)
names(glut_cl) = colnames(axolotl_glut)
other = axolotl_all$classification_2[!(colnames(axolotl_all) %in% c(names(glut_cl), names(gaba_cl)))]
all_annot = data.frame(\all_annot\ = c(other, gaba_cl, glut_cl))
axolotl_all = AddMetaData(axolotl_all, metadata = all_annot)
axolotl_all_filt = axolotl_all[,!is.na(axolotl_all$all_annot) & !grepl(\neuron\, axolotl_all$all_annot)]
<!-- rnb-source-end -->
<!-- rnb-chunk-end -->
<!-- rnb-text-begin -->
# Prepare datasets
Reformat matrices to have only one2one orthologs that are shared
<!-- rnb-text-end -->
<!-- rnb-chunk-begin -->
<!-- rnb-source-begin eyJkYXRhIjoiYGBgclxuYGBgclxubWV0YV9saXN0ID0gbGlzdChcXGV4cF90dXJcXCA9IHR1cnRsZV9hbGxAbWV0YS5kYXRhLCBcXGV4cF9saXpcXCA9IGxpemFyZF9hbGxAbWV0YS5kYXRhLCBcbiAgICAgICAgICAgICAgICAgXFxleHBfZHJlXFwgPSBkcmVyaW9fYnJhaW5AbWV0YS5kYXRhJENsdXN0ZXIyLjUsIFxcZXhwX3plZlxcID0gZmluY2hlc19saXN0JFpGQG1ldGEuZGF0YSwgXG4gICAgICAgICAgICAgICAgIFxcZXhwX2JlZlxcID0gZmluY2hlc19saXN0JFpGQG1ldGEuZGF0YSwgXFxleHBfbW91XFwgPSBtb3VzZV9icmFpbkBtZXRhLmRhdGEsIFxuICAgICAgICAgICAgICAgICBcXGV4cF9heG9cXCA9IGF4b2xvdGxfYWxsX2ZpbHRAbWV0YS5kYXRhKVxuY2xfaWRfbGlzdCA9IGxpc3QoXFxjbHVzdGVyXFwsIFxcY2x1c3RlcnNcXCwgXFxNb2QyLjVcXCwgXFxzZXVyYXRfY2x1c3RlcnNcXCwgXFxzZXVyYXRfY2x1c3RlcnNcXCwgXFxUYXhvbm9teV9ncm91cFxcLFxuICAgICAgICAgICAgICAgICAgXFxhbGxfYW5ub3RcXClcbm5hbWVzKGNsX2lkX2xpc3QpID0gbmFtZXMobWV0YV9saXN0KVxuYGBgXG5gYGAifQ== -->
```r
```r
meta_list = list(\exp_tur\ = turtle_all@meta.data, \exp_liz\ = lizard_all@meta.data,
\exp_dre\ = drerio_brain@meta.data$Cluster2.5, \exp_zef\ = finches_list$ZF@meta.data,
\exp_bef\ = finches_list$ZF@meta.data, \exp_mou\ = mouse_brain@meta.data,
\exp_axo\ = axolotl_all_filt@meta.data)
cl_id_list = list(\cluster\, \clusters\, \Mod2.5\, \seurat_clusters\, \seurat_clusters\, \Taxonomy_group\,
\all_annot\)
names(cl_id_list) = names(meta_list)
<!-- rnb-source-end -->
<!-- rnb-chunk-end -->
<!-- rnb-text-begin -->
Renormalise, get mean expression and markers
<!-- rnb-text-end -->
<!-- rnb-chunk-begin -->
<!-- rnb-source-begin eyJkYXRhIjoiYGBgclxuc2V1cmF0X29uZTJvbmUgPSBsaXN0KClcbmF2Z19leHAgPSBsaXN0KClcbm1rX2xpc3QgPSBsaXN0KClcbmZvcihuIGluIG5hbWVzKGNvdW50c19saXN0KSl7XG4gIHByaW50KG4pXG4gIHNldXJhdF9vbmUyb25lW1tuXV0gPSBDcmVhdGVTZXVyYXRPYmplY3QoY291bnRzX2xpc3RbW25dXSwgbWV0YS5kYXRhID0gbWV0YV9saXN0W1tuXV0pXG4gIElkZW50cyhzZXVyYXRfb25lMm9uZVtbbl1dKSA9IHNldXJhdF9vbmUyb25lW1tuXV1AbWV0YS5kYXRhWyxjbF9pZF9saXN0W1tuXV1dXG4gIHNldXJhdF9vbmUyb25lW1tuXV0gPSBzdXBwcmVzc1dhcm5pbmdzKFNDVHJhbnNmb3JtKHNldXJhdF9vbmUyb25lW1tuXV0sIGRvLmNvcnJlY3QudW1pID0gVCwgdmVyYm9zZSA9IEYsIFxuICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBzZWVkLnVzZSA9IDEsIHZhcnMudG8ucmVncmVzcyA9IFwibkNvdW50X1JOQVwiLFxuICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB2YXJpYWJsZS5mZWF0dXJlcy5ydi50aCA9IDEsIHJldHVybi5vbmx5LnZhci5nZW5lcyA9IEYsXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHZhcmlhYmxlLmZlYXR1cmVzLm4gPSBOVUxMKSlcbiAgYXZnX2V4cFtbbl1dID0gQXZlcmFnZUV4cHJlc3Npb24oc2V1cmF0X29uZTJvbmVbW25dXSwgYXNzYXlzID0gXCJTQ1RcIikkU0NUXG4gIG1rX2xpc3RbW25dXSA9IEZpbmRBbGxNYXJrZXJzKHNldXJhdF9vbmUyb25lW1tuXV0sIGxvZ2ZjLnRocmVzaG9sZCA9IDAuMiwgb25seS5wb3MgPSBULFxuICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBwc2V1ZG9jb3VudC51c2UgPSAwLjEsIGFzc2F5ID0gXCJTQ1RcIilcbn1cbnNhdmUoc2V1cmF0X29uZTJvbmUsIGF2Z19leHAsIG1rX2xpc3QsIGZpbGUgPSBcInJlc3VsdHMvY3Jvc3Nfc3BlY2llc19jb3JyZWxhdGlvbi9vbmUyb25lX3NldXJhdF9hdmdfbWsuUkRhdGFcIilcbmBgYCJ9 -->
```r
seurat_one2one = list()
avg_exp = list()
mk_list = list()
for(n in names(counts_list)){
print(n)
seurat_one2one[[n]] = CreateSeuratObject(counts_list[[n]], meta.data = meta_list[[n]])
Idents(seurat_one2one[[n]]) = seurat_one2one[[n]]@meta.data[,cl_id_list[[n]]]
seurat_one2one[[n]] = suppressWarnings(SCTransform(seurat_one2one[[n]], do.correct.umi = T, verbose = F,
seed.use = 1, vars.to.regress = "nCount_RNA",
variable.features.rv.th = 1, return.only.var.genes = F,
variable.features.n = NULL))
avg_exp[[n]] = AverageExpression(seurat_one2one[[n]], assays = "SCT")$SCT
mk_list[[n]] = FindAllMarkers(seurat_one2one[[n]], logfc.threshold = 0.2, only.pos = T,
pseudocount.use = 0.1, assay = "SCT")
}
save(seurat_one2one, avg_exp, mk_list, file = "results/cross_species_correlation/one2one_seurat_avg_mk.RData")
Correlation analysis
Load data
load("results/cross_species_correlation/one2one_seurat_avg_mk.RData")
Select genes to use for correlations, normalise data
load("results/cross_species_correlation/one2one_seurat_avg_mk.RData")
upset(fromList(top_mk_list[c(6,4,5,2,1,7,3)]), sets = names(top_mk_list)[c(6,4,5,2,1,7,3)],
order.by = "freq", keep.order = TRUE, number.angles = 0, nintersects = 30, point.size = 1.8)
Calculate correlations
Plot correlation matrices
sp_cor_l = list()
for(i in names(avg_exp_sub)){
for(j in names(avg_exp_sub)){
print(paste(i, j))
sp_cor_l[[paste0(i, ".", j)]] = cor(avg_exp_sub[[i]], avg_exp_sub[[j]], method = "sp")
}
}
[1] "exp_tur exp_tur"
[1] "exp_tur exp_liz"
[1] "exp_tur exp_dre"
[1] "exp_tur exp_zef"
[1] "exp_tur exp_bef"
[1] "exp_tur exp_mou"
[1] "exp_tur exp_axo"
[1] "exp_liz exp_tur"
[1] "exp_liz exp_liz"
[1] "exp_liz exp_dre"
[1] "exp_liz exp_zef"
[1] "exp_liz exp_bef"
[1] "exp_liz exp_mou"
[1] "exp_liz exp_axo"
[1] "exp_dre exp_tur"
[1] "exp_dre exp_liz"
[1] "exp_dre exp_dre"
[1] "exp_dre exp_zef"
[1] "exp_dre exp_bef"
[1] "exp_dre exp_mou"
[1] "exp_dre exp_axo"
[1] "exp_zef exp_tur"
[1] "exp_zef exp_liz"
[1] "exp_zef exp_dre"
[1] "exp_zef exp_zef"
[1] "exp_zef exp_bef"
[1] "exp_zef exp_mou"
[1] "exp_zef exp_axo"
[1] "exp_bef exp_tur"
[1] "exp_bef exp_liz"
[1] "exp_bef exp_dre"
[1] "exp_bef exp_zef"
[1] "exp_bef exp_bef"
[1] "exp_bef exp_mou"
[1] "exp_bef exp_axo"
[1] "exp_mou exp_tur"
[1] "exp_mou exp_liz"
[1] "exp_mou exp_dre"
[1] "exp_mou exp_zef"
[1] "exp_mou exp_bef"
[1] "exp_mou exp_mou"
[1] "exp_mou exp_axo"
[1] "exp_axo exp_tur"
[1] "exp_axo exp_liz"
[1] "exp_axo exp_dre"
[1] "exp_axo exp_zef"
[1] "exp_axo exp_bef"
[1] "exp_axo exp_mou"
[1] "exp_axo exp_axo"
UMAP from mean expression
cols = colorRampPalette(c(rev(RColorBrewer::brewer.pal(9, "Blues")),
RColorBrewer::brewer.pal(9, "Reds")))(100)
for(n in names(sp_cor_l)){
pltmat = t(sp_cor_l[[n]])
br = c(seq(min(pltmat), 0, length.out = 51), seq(0, max(pltmat), length.out = 51)[-1])
pheatmap::pheatmap(pltmat, breaks = br, color = cols,
clustering_method = "ward.D2", main = n)
}

















































Network in MDS from correlations
cor_tab_l = list()
for(n in names(sp_cor_l)){
dat = reshape2::melt(sp_cor_l[[n]])
dat$sp1 = strsplit(strsplit(n, ".", fixed = T)[[1]][1], "_", fixed = T)[[1]][2]
dat$sp2 = strsplit(strsplit(n, ".", fixed = T)[[1]][2], "_", fixed = T)[[1]][2]
dat$Var1 = paste0(dat$sp1, "_", dat$Var1)
dat$Var2 = paste0(dat$sp2, "_", dat$Var2)
cor_tab_l[[n]] = dat
}
cor_tab = Reduce(rbind, cor_tab_l)
cor_sp = reshape2::dcast(cor_tab, Var1 ~ Var2, value.var = "value")
rownames(cor_sp) = cor_sp[,1]
cor_sp = cor_sp[,-1]
adj_mat = cor_sp>=0.3
# build graph, project with MDS
network_sp = graph_from_adjacency_matrix(adj_mat, weighted=T, mode="undirected", diag=F)
l_sp = igraph::layout_with_mds(network_sp)
l_sp = data.frame(l_sp)
l_sp$cl = colnames(adj_mat)
rownames(l_sp) = colnames(adj_mat)
l_sp$sp = unlist(lapply(strsplit(l_sp$cl, "_"), function(x) x[1]))
l_sp$cl = unlist(lapply(strsplit(l_sp$cl, "_"), function(x) x[2]))
df_names = l_sp[l_sp$sp=="axo",]
ggplot(l_sp, aes(x = X1, y = X2, colour = sp))+
geom_point()+
ggrepel::geom_text_repel(data = df_names, mapping = aes(x = X1, y = X2, label = cl), max.overlaps = 100)+
theme_classic()
common_genes = Reduce(intersect, top_mk_list)
any_genes = Reduce(union, top_mk_list)
non_shared_genes = any_genes[!(any_genes %in% common_genes)]
non_shared_genes = non_shared_genes[non_shared_genes %in% Reduce(intersect, lapply(avg_exp, rownames))]
avg_exp_sub = lapply(avg_exp, function(x) t(apply(x[non_shared_genes,], 1, function(y) y/mean(y))))
lapply(avg_exp_sub, dim)
for(n in names(avg_exp_sub)){
sp = strsplit(n, "_")[[1]][2]
colnames(avg_exp_sub[[n]]) = paste0(sp, "..", colnames(avg_exp_sub[[n]]))
}
avg_exp_all = t(Reduce(cbind, avg_exp_sub))
set.seed(2954)
l = uwot::umap(avg_exp_all, metric = "cosine", ret_nn = T, n_epochs = 1000)
l_sp = data.frame(l$embedding)
rownames(l_sp) = rownames(avg_exp_all)
l_sp$cl = rownames(avg_exp_all)
l_sp$sp = unlist(lapply(strsplit(l_sp$cl, "..", fixed = T), function(x) x[1]))
l_sp$cl = unlist(lapply(strsplit(l_sp$cl, "..", fixed = T), function(x) x[2]))
df_names = l_sp[l_sp$sp=="axo",]
ggplot(l_sp, aes(x = X1, y = X2, colour = sp))+
geom_point()+
ggrepel::geom_text_repel(data = df_names, mapping = aes(x = X1, y = X2, label = cl),
max.overlaps = 100, size = 3)+
theme_classic()
LS0tCnRpdGxlOiAiQ3Jvc3Mgc3BlY2llcyBjb3JyZWxhdGlvbiIKb3V0cHV0OiBodG1sX25vdGVib29rCi0tLQoKTm90ZWJvb2sgZm9yIGNlbGwgdHlwZSBpZGVudGl0eSBjb3JyZWxhdGlvbnMKCgoKIyBHZW5lcmFsIFNldHVwClNldHVwIGNodW5rCgpgYGB7ciwgc2V0dXAsIGluY2x1ZGU9RkFMU0V9CmtuaXRyOjpvcHRzX2NodW5rJHNldChmaWcud2lkdGggPSA4KQprbml0cjo6b3B0c19rbml0JHNldChyb290LmRpciA9IG5vcm1hbGl6ZVBhdGgoIi4uIikpCmtuaXRyOjpvcHRzX2tuaXQkZ2V0KCJyb290LmRpciIpCmBgYAoKU2V0dXAgcmV0aWN1bGF0ZQoKYGBge3J9CmxpYnJhcnkocmV0aWN1bGF0ZSkKa25pdHI6OmtuaXRfZW5naW5lcyRzZXQocHl0aG9uID0gcmV0aWN1bGF0ZTo6ZW5nX3B5dGhvbikKcHlfYXZhaWxhYmxlKGluaXRpYWxpemUgPSBGQUxTRSkKdXNlX3B5dGhvbihTeXMud2hpY2goInB5dGhvbiIpKQpweV9jb25maWcoKQpgYGAKCkxvYWQgbGlicmFyaWVzCgpgYGB7cn0KbGlicmFyeShTZXVyYXQpCmxpYnJhcnkoZHBseXIpCmxpYnJhcnkodGlkeXZlcnNlKQpsaWJyYXJ5KGlncmFwaCkKbGlicmFyeShVcFNldFIpCmBgYAoKCgojIExvYWQgYW5kIHJlZm9ybWF0IGRhdGEKTG9hZCBTZXVyYXQgZGF0YQoKYGBge3J9CmxpemFyZF9hbGwgPSByZWFkUkRTKCJkYXRhL2V4cHJlc3Npb24vbGl6YXJkX2FsbF92My5SRFMiKQp0dXJ0bGVfYWxsID0gcmVhZFJEUygiZGF0YS9leHByZXNzaW9uL3R1cnRsZV9hbGxfdjMuUkRTIikKZHJlcmlvX2JyYWluID0gcmVhZFJEUygiZGF0YS9leHByZXNzaW9uL2RyZXJpb19icmFpbl92My5SRFMiKQpmaW5jaGVzX2JyYWluID0gcmVhZFJEUygiZGF0YS9leHByZXNzaW9uL0hWQ19SQV9YLlJEUyIpCmZpbmNoZXNfbGlzdCA9IFNwbGl0T2JqZWN0KGZpbmNoZXNfYnJhaW4sIHNwbGl0LmJ5ID0gInNwZWNpZXMiKQptb3VzZV9icmFpbiA9IHJlYWRSRFMoImRhdGEvZXhwcmVzc2lvbi9sNV9hbGxfc2V1cmF0LlJEUyIpCmBgYAoKTG9hZCBheG9sb3RsIGRhdGEKCmBgYHtyfQpheG9sb3RsX2dhYmEgPSByZWFkUkRTKGZpbGUgPSAiZGF0YS9leHByZXNzaW9uL2F4b2xvdGwvcGFsbGl1bV9uZXVyb25hbF9HQUJBX3JlczA3X2hhcm1vbnkuUkRTIikKYXhvbG90bF9nbHV0ID0gcmVhZFJEUyhmaWxlID0gImRhdGEvZXhwcmVzc2lvbi9heG9sb3RsL3BhbGxpdW1fbmV1cm9uYWxfR2x1dF9yZXMwNV9oYXJtb255LlJEUyIpCmF4b2xvdGxfbmV1cm9ucyA9IHJlYWRSRFMoZmlsZSA9ICJkYXRhL2V4cHJlc3Npb24vYXhvbG90bC9wYWxsaXVtX25ldXJvbmFsX3JlczA3X2hhcm1vbnkuUkRTIikKYXhvbG90bF9hbGwgPSByZWFkUkRTKGZpbGUgPSAiZGF0YS9leHByZXNzaW9uL2F4b2xvdGwvcGFsbGl1bXJlczA3X2hhcm1vbnkuUkRTIikKYGBgCgpNZXJnZSBheG9sb3RsIGFubm90YXRpb25zIHRvZ2V0aGVyIGludG8gYSBzaW5nbGUgb2JqZWN0CgpgYGB7cn0KZ2FiYV9jbCA9IHBhc3RlMCgiR0FCQV8iLCBheG9sb3RsX2dhYmEkc2V1cmF0X2NsdXN0ZXJzKQpuYW1lcyhnYWJhX2NsKSA9IGNvbG5hbWVzKGF4b2xvdGxfZ2FiYSkKZ2x1dF9jbCA9IHBhc3RlMCgiR2x1dF8iLCBheG9sb3RsX2dsdXQkc2V1cmF0X2NsdXN0ZXJzKQpuYW1lcyhnbHV0X2NsKSA9IGNvbG5hbWVzKGF4b2xvdGxfZ2x1dCkKb3RoZXIgPSBheG9sb3RsX2FsbCRjbGFzc2lmaWNhdGlvbl8yWyEoY29sbmFtZXMoYXhvbG90bF9hbGwpICVpbiUgYyhuYW1lcyhnbHV0X2NsKSwgbmFtZXMoZ2FiYV9jbCkpKV0KYWxsX2Fubm90ID0gZGF0YS5mcmFtZSgiYWxsX2Fubm90IiA9IGMob3RoZXIsIGdhYmFfY2wsIGdsdXRfY2wpKQpheG9sb3RsX2FsbCA9IEFkZE1ldGFEYXRhKGF4b2xvdGxfYWxsLCBtZXRhZGF0YSA9IGFsbF9hbm5vdCkKYXhvbG90bF9hbGxfZmlsdCA9IGF4b2xvdGxfYWxsWywhaXMubmEoYXhvbG90bF9hbGwkYWxsX2Fubm90KSAmICFncmVwbCgibmV1cm9uIiwgYXhvbG90bF9hbGwkYWxsX2Fubm90KV0KYGBgCgpPcmdhbmlzZSBtZXRhZGF0YSBpbnRvIGEgbGlzdAoKYGBge3J9Cm1ldGFfbGlzdCA9IGxpc3QoImV4cF90dXIiID0gdHVydGxlX2FsbEBtZXRhLmRhdGEsICJleHBfbGl6IiA9IGxpemFyZF9hbGxAbWV0YS5kYXRhLCAKICAgICAgICAgICAgICAgICAiZXhwX2RyZSIgPSBkcmVyaW9fYnJhaW5AbWV0YS5kYXRhLCAiZXhwX3plZiIgPSBmaW5jaGVzX2xpc3QkWkZAbWV0YS5kYXRhLCAKICAgICAgICAgICAgICAgICAiZXhwX2JlZiIgPSBmaW5jaGVzX2xpc3QkQkZAbWV0YS5kYXRhLCAiZXhwX21vdSIgPSBtb3VzZV9icmFpbkBtZXRhLmRhdGEsIAogICAgICAgICAgICAgICAgICJleHBfYXhvIiA9IGF4b2xvdGxfYWxsX2ZpbHRAbWV0YS5kYXRhKQpjbF9pZF9saXN0ID0gbGlzdCgiY2x1c3RlciIsICJjbHVzdGVycyIsICJNb2QyLjUiLCAic2V1cmF0X2NsdXN0ZXJzIiwgInNldXJhdF9jbHVzdGVycyIsICJUYXhvbm9teV9ncm91cCIsCiAgICAgICAgICAgICAgICAgICJhbGxfYW5ub3QiKQpuYW1lcyhjbF9pZF9saXN0KSA9IG5hbWVzKG1ldGFfbGlzdCkKYGBgCgpMb2FkIG5lY2Vzc2FyeSBlZ2dOT0cgZGF0YQoKYGBge3J9CmFubm90X2xpc3QgPSByZWFkUkRTKGZpbGUgPSAiZGF0YS9lZ2dOT0cvYW5ub3RhdGlvbl9saXN0LlJEUyIpCmBgYAoKCgojIFByZXBhcmUgZGF0YXNldHMKUmVmb3JtYXQgbWF0cmljZXMgdG8gaGF2ZSBvbmx5IG9uZTJvbmUgb3J0aG9sb2dzIHRoYXQgYXJlIHNoYXJlZAoKYGBge3J9CmNvdW50c19saXN0ID0gbGlzdChleHBfdHVyID0gdHVydGxlX2FsbEBhc3NheXMkUk5BQGNvdW50cywgZXhwX2xpeiA9IGxpemFyZF9hbGxAYXNzYXlzJFJOQUBjb3VudHMsCiAgICAgICAgICAgICAgICAgICBleHBfZHJlID0gZHJlcmlvX2JyYWluQGFzc2F5cyRSTkFAY291bnRzLCBleHBfemVmID0gZmluY2hlc19saXN0JFpGQGFzc2F5cyRSTkFAY291bnRzLAogICAgICAgICAgICAgICAgICAgZXhwX2JlZiA9IGZpbmNoZXNfbGlzdCRCRkBhc3NheXMkUk5BQGNvdW50cywgZXhwX21vdSA9IG1vdXNlX2JyYWluQGFzc2F5cyRSTkFAY291bnRzLAogICAgICAgICAgICAgICAgICAgZXhwX2F4byA9IGF4b2xvdGxfYWxsX2ZpbHRAYXNzYXlzJFJOQUBjb3VudHMpCgpyZWZvcm1hdFdpdGhPcnRoID0gZnVuY3Rpb24oZWdnbm9nX2Fubm90LCBjb3VudHNfbCwgc3BfbGlzdCA9IGMoIkNwaWN0YSIsICJQdml0dGljZXBzIikpewogIGxfZ19zcCA9IGxpc3QoKQogIGZvcihzcGkgaW4gMTpsZW5ndGgoc3BfbGlzdCkpewogICAgY3AgPSBuYW1lcyhjb3VudHNfbClbc3BpXQogICAgc3AgPSBzcF9saXN0W3NwaV0KICAgIGxfZ19zcFtbY3BdXSA9IGVnZ25vZ19hbm5vdFtbc3BdXVssYygyLDUpXQogICAgbF9nX3NwW1tjcF1dID0gbF9nX3NwW1tjcF1dW2xfZ19zcFtbY3BdXSRQcmVmZXJyZWRfbmFtZSE9Ii0iICYgIWlzLm5hKGxfZ19zcFtbY3BdXSRQcmVmZXJyZWRfbmFtZSksXQogICAgbF9nX3NwW1tjcF1dJFByZWZlcnJlZF9uYW1lID0gdG91cHBlcihsX2dfc3BbW2NwXV0kUHJlZmVycmVkX25hbWUpCiAgICBsX2dfc3BbW2NwXV0gPSB1bmlxdWUobF9nX3NwW1tjcF1dKQogICAgbF9nX3NwW1tjcF1dID0gbF9nX3NwW1tjcF1dWyEobF9nX3NwW1tjcF1dJGdlbmUgJWluJSBsX2dfc3BbW3NwXV0kZ2VuZVtkdXBsaWNhdGVkKGxfZ19zcFtbY3BdXSRnZW5lKV0pLF0KICAgIGxfZ19zcFtbY3BdXSA9IGxfZ19zcFtbY3BdXVshKGxfZ19zcFtbY3BdXSRQcmVmZXJyZWRfbmFtZSAlaW4lIGxfZ19zcFtbY3BdXSRQcmVmZXJyZWRfbmFtZVtkdXBsaWNhdGVkKGxfZ19zcFtbY3BdXSRQcmVmZXJyZWRfbmFtZSldKSxdCiAgfQogIAogIGFsbF9zcF9tYXRjaCA9IGxfZ19zcCAlPiUgcmVkdWNlKGZ1bGxfam9pbiwgYnkgPSAiUHJlZmVycmVkX25hbWUiKQogIGFsbF9zcF9tYXRjaCA9IGFsbF9zcF9tYXRjaFssYygyLDEsMzpuY29sKGFsbF9zcF9tYXRjaCkpXQogIGNvbG5hbWVzKGFsbF9zcF9tYXRjaCkgPSBjKCJQcmVmZXJyZWRfbmFtZSIsIG5hbWVzKGNvdW50c19sKSkKICAKICAjIG9ubHkga2VlcCBnZW5lcyBhcHBlYXJpbmcgaW4gZXhwIG1hdHJpeAogIGdfaW4gPSBsYXBwbHkoY29sbmFtZXMoYWxsX3NwX21hdGNoKVstMV0sIAogICAgICAgICAgICAgICAgZnVuY3Rpb24oeCkgdG91cHBlcihhbGxfc3BfbWF0Y2hbLHhdKSAlaW4lIHRvdXBwZXIocm93bmFtZXMoY291bnRzX2xbW3hdXSkpKQogIGtlZXAgPSByZXAoVCwgbnJvdyhhbGxfc3BfbWF0Y2gpKQogIGZvcihuIGluIDE6bGVuZ3RoKGdfaW4pKXsKICAgIGtlZXAgPSBrZWVwICYgZ19pbltbbl1dCiAgfQogIGFsbF9zcF9tYXRjaF9maWx0ID0gYWxsX3NwX21hdGNoW2tlZXAsXQogIAogICMgcmVtb3ZlIGR1cGxpY2F0ZWQgLSBvbmx5IHdhbnQgb25lMm9uZQogIGdfZHUgPSBsYXBwbHkoY29sbmFtZXMoYWxsX3NwX21hdGNoX2ZpbHQpWy0xXSwgCiAgICAgICAgICAgICAgICBmdW5jdGlvbih4KSAhKGFsbF9zcF9tYXRjaF9maWx0Wyx4XSAlaW4lIGFsbF9zcF9tYXRjaF9maWx0Wyx4XVtkdXBsaWNhdGVkKGFsbF9zcF9tYXRjaF9maWx0Wyx4XSldKSkKICBrZWVwID0gcmVwKFQsIG5yb3coYWxsX3NwX21hdGNoX2ZpbHQpKQogIGZvcihuIGluIDE6bGVuZ3RoKGdfZHUpKXsKICAgIGtlZXAgPSBrZWVwICYgZ19kdVtbbl1dCiAgfQogIGFsbF9zcF9tYXRjaF9maWx0ID0gYWxsX3NwX21hdGNoX2ZpbHRba2VlcCxdCiAgCiAgIyBwdXR0aW5nIGV2ZXJ5dGhpbmcgaW50byB1cHBlcmNhc2UgYmVjYXVzZSB0aGUgZHJlcmlvIGRhdGEgaXMgaW4gdXBwZXJjYXNlIGZvciBzb21lIHJlYXNvbi4uLgogIGZvcihuIGluIGNvbG5hbWVzKGFsbF9zcF9tYXRjaF9maWx0KSl7YWxsX3NwX21hdGNoX2ZpbHRbLG5dID0gdG91cHBlcihhbGxfc3BfbWF0Y2hfZmlsdFssbl0pfQogIAogIHJldHVybihhbGxfc3BfbWF0Y2hfZmlsdCkKfQoKZ2VuZV9tYXRjaCA9IHJlZm9ybWF0V2l0aE9ydGgoYW5ub3RfbGlzdCwgY291bnRzX2xpc3QsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICBzcF9saXN0ID0gYygiQ3BpY3RhIiwgIlB2aXR0aWNlcHMiLCAiRHJlcmlvIiwgIlRndXR0YXRhIiwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJUZ3V0dGF0YSIsICJNbXVzY3VsdXMiLCAiQW1leGljYW51bSIpKQpnZW5lX21hdGNoX2NvbXAgPSBnZW5lX21hdGNoW2NvbXBsZXRlLmNhc2VzKGdlbmVfbWF0Y2gpLF0KCmxhcHBseShjb3VudHNfbGlzdCwgZGltKQpmb3IobiBpbiBuYW1lcyhjb3VudHNfbGlzdCkpewogIGNvdW50c19saXN0W1tuXV0gPSBjb3VudHNfbGlzdFtbbl1dW3RvdXBwZXIocm93bmFtZXMoY291bnRzX2xpc3RbW25dXSkpICVpbiUgZ2VuZV9tYXRjaF9jb21wWyxuXSxdCiAgcm93bmFtZXMoY291bnRzX2xpc3RbW25dXSkgPSBnZW5lX21hdGNoX2NvbXAkUHJlZmVycmVkX25hbWVbbWF0Y2godG91cHBlcihyb3duYW1lcyhjb3VudHNfbGlzdFtbbl1dKSksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZ2VuZV9tYXRjaF9jb21wWyxuXSldCn0KbGFwcGx5KGNvdW50c19saXN0LCBkaW0pCgojIG1ha2Ugc3VyZSB0aGV5IGhhdmUgdGhlIHNhbWUgb3JkZXJpbmcKY291bnRzX2xpc3QgPSBsYXBwbHkoY291bnRzX2xpc3QsIGZ1bmN0aW9uKHgpIHhbcm93bmFtZXMoY291bnRzX2xpc3QkZXhwX2F4byksXSkKYGBgCgpSZW5vcm1hbGlzZSwgZ2V0IG1lYW4gZXhwcmVzc2lvbiBhbmQgbWFya2VycwoKYGBge3J9CnNldXJhdF9vbmUyb25lID0gbGlzdCgpCmF2Z19leHAgPSBsaXN0KCkKbWtfbGlzdCA9IGxpc3QoKQpmb3IobiBpbiBuYW1lcyhjb3VudHNfbGlzdCkpewogIHByaW50KG4pCiAgc2V1cmF0X29uZTJvbmVbW25dXSA9IENyZWF0ZVNldXJhdE9iamVjdChjb3VudHNfbGlzdFtbbl1dLCBtZXRhLmRhdGEgPSBtZXRhX2xpc3RbW25dXSkKICBJZGVudHMoc2V1cmF0X29uZTJvbmVbW25dXSkgPSBzZXVyYXRfb25lMm9uZVtbbl1dQG1ldGEuZGF0YVssY2xfaWRfbGlzdFtbbl1dXQogIHNldXJhdF9vbmUyb25lW1tuXV0gPSBzdXBwcmVzc1dhcm5pbmdzKFNDVHJhbnNmb3JtKHNldXJhdF9vbmUyb25lW1tuXV0sIGRvLmNvcnJlY3QudW1pID0gVCwgdmVyYm9zZSA9IEYsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHNlZWQudXNlID0gMSwgdmFycy50by5yZWdyZXNzID0gIm5Db3VudF9STkEiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHZhcmlhYmxlLmZlYXR1cmVzLnJ2LnRoID0gMSwgcmV0dXJuLm9ubHkudmFyLmdlbmVzID0gRiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB2YXJpYWJsZS5mZWF0dXJlcy5uID0gTlVMTCkpCiAgYXZnX2V4cFtbbl1dID0gQXZlcmFnZUV4cHJlc3Npb24oc2V1cmF0X29uZTJvbmVbW25dXSwgYXNzYXlzID0gIlNDVCIpJFNDVAogIG1rX2xpc3RbW25dXSA9IEZpbmRBbGxNYXJrZXJzKHNldXJhdF9vbmUyb25lW1tuXV0sIGxvZ2ZjLnRocmVzaG9sZCA9IDAuMiwgb25seS5wb3MgPSBULAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHBzZXVkb2NvdW50LnVzZSA9IDAuMSwgYXNzYXkgPSAiU0NUIikKfQpzYXZlKHNldXJhdF9vbmUyb25lLCBhdmdfZXhwLCBta19saXN0LCBmaWxlID0gInJlc3VsdHMvY3Jvc3Nfc3BlY2llc19jb3JyZWxhdGlvbi9vbmUyb25lX3NldXJhdF9hdmdfbWsuUkRhdGEiKQpgYGAKCgoKIyBDb3JyZWxhdGlvbiBhbmFseXNpcwpMb2FkIGRhdGEKCmBgYHtyfQpsb2FkKCJyZXN1bHRzL2Nyb3NzX3NwZWNpZXNfY29ycmVsYXRpb24vb25lMm9uZV9zZXVyYXRfYXZnX21rLlJEYXRhIikKYGBgCgpTZWxlY3QgZ2VuZXMgdG8gdXNlIGZvciBjb3JyZWxhdGlvbnMsIG5vcm1hbGlzZSBkYXRhCgpgYGB7cn0KIyBnZXQgdG9wIG1hcmtlcnMKdG9wX21rX2xpc3QgPSBsYXBwbHkobWtfbGlzdCwgZnVuY3Rpb24oeCkgeFt4JHBfdmFsX2Fkajw9MC4wNSAmIHgkYXZnX2xvZzJGQz4wLjIsXSkKZm9yKG4gaW4gbmFtZXModG9wX21rX2xpc3QpKXsKICB0b3BfbWtfbGlzdFtbbl1dID0gdW5pcXVlKHVubGlzdCgodG9wX21rX2xpc3RbW25dXSAlPiUgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgYXJyYW5nZShkZXNjKGF2Z19sb2cyRkMpKSAlPiUgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZ3JvdXBfYnkoY2x1c3RlcikgJT4lIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHNsaWNlKDE6NTAwMDAwKSlbLCJnZW5lIl0pKQp9Cgpjb21tb25fZ2VuZXMgPSBSZWR1Y2UoaW50ZXJzZWN0LCB0b3BfbWtfbGlzdCkKY29tbW9uX2dlbmVzID0gY29tbW9uX2dlbmVzW2NvbW1vbl9nZW5lcyAlaW4lIFJlZHVjZShpbnRlcnNlY3QsIGxhcHBseShhdmdfZXhwLCByb3duYW1lcykpXQphdmdfZXhwX3N1YiA9IGxhcHBseShhdmdfZXhwLCBmdW5jdGlvbih4KSB0KGFwcGx5KHhbY29tbW9uX2dlbmVzLF0sIDEsIGZ1bmN0aW9uKHkpIHkvbWVhbih5KSkpKQpsYXBwbHkoYXZnX2V4cF9zdWIsIGRpbSkKYGBgCgoKCmBgYHtyfQp1cHNldChmcm9tTGlzdCh0b3BfbWtfbGlzdFtjKDYsNCw1LDIsMSw3LDMpXSksIHNldHMgPSBuYW1lcyh0b3BfbWtfbGlzdClbYyg2LDQsNSwyLDEsNywzKV0sIAogICAgICBvcmRlci5ieSA9ICJmcmVxIiwga2VlcC5vcmRlciA9IFRSVUUsIG51bWJlci5hbmdsZXMgPSAwLCBuaW50ZXJzZWN0cyA9IDMwLCBwb2ludC5zaXplID0gMS44KQpgYGAKCkNhbGN1bGF0ZSBjb3JyZWxhdGlvbnMKCmBgYHtyLCBpbmNsdWRlID0gRkFMU0V9CnNwX2Nvcl9sID0gbGlzdCgpCmZvcihpIGluIG5hbWVzKGF2Z19leHBfc3ViKSl7CiAgZm9yKGogaW4gbmFtZXMoYXZnX2V4cF9zdWIpKXsKICAgIHByaW50KHBhc3RlKGksIGopKQogICAgc3BfY29yX2xbW3Bhc3RlMChpLCAiLiIsIGopXV0gPSBjb3IoYXZnX2V4cF9zdWJbW2ldXSwgYXZnX2V4cF9zdWJbW2pdXSwgbWV0aG9kID0gInNwIikKICB9Cn0KYGBgCgpQbG90IGNvcnJlbGF0aW9uIG1hdHJpY2VzCgpgYGB7cn0KY29scyA9IGNvbG9yUmFtcFBhbGV0dGUoYyhyZXYoUkNvbG9yQnJld2VyOjpicmV3ZXIucGFsKDksICJCbHVlcyIpKSwKICAgICAgICAgICAgICAgICAgICAgICAgICBSQ29sb3JCcmV3ZXI6OmJyZXdlci5wYWwoOSwgIlJlZHMiKSkpKDEwMCkKZm9yKG4gaW4gbmFtZXMoc3BfY29yX2wpKXsKICBwbHRtYXQgPSB0KHNwX2Nvcl9sW1tuXV0pCiAgYnIgPSBjKHNlcShtaW4ocGx0bWF0KSwgMCwgbGVuZ3RoLm91dCA9IDUxKSwgc2VxKDAsIG1heChwbHRtYXQpLCBsZW5ndGgub3V0ID0gNTEpWy0xXSkKICBwaGVhdG1hcDo6cGhlYXRtYXAocGx0bWF0LCBicmVha3MgPSBiciwgY29sb3IgPSBjb2xzLAogICAgICAgICAgICAgICAgICAgICBjbHVzdGVyaW5nX21ldGhvZCA9ICJ3YXJkLkQyIiwgbWFpbiA9IG4pCn0KYGBgCgpVTUFQIGZyb20gbWVhbiBleHByZXNzaW9uCgpgYGB7cn0KZm9yKG4gaW4gbmFtZXMoYXZnX2V4cF9zdWIpKXsKICBzcCA9IHN0cnNwbGl0KG4sICJfIilbWzFdXVsyXQogIGNvbG5hbWVzKGF2Z19leHBfc3ViW1tuXV0pID0gcGFzdGUwKHNwLCAiLi4iLCBjb2xuYW1lcyhhdmdfZXhwX3N1Yltbbl1dKSkKfQphdmdfZXhwX2FsbCA9IHQoUmVkdWNlKGNiaW5kLCBhdmdfZXhwX3N1YikpCnNldC5zZWVkKDI5NTQpCmwgPSB1d290Ojp1bWFwKGF2Z19leHBfYWxsLCBtZXRyaWMgPSAiY29zaW5lIiwgcmV0X25uID0gVCwgbl9lcG9jaHMgPSAxMDAwKQpsX3NwID0gZGF0YS5mcmFtZShsJGVtYmVkZGluZykKcm93bmFtZXMobF9zcCkgPSByb3duYW1lcyhhdmdfZXhwX2FsbCkKbF9zcCRjbCA9IHJvd25hbWVzKGF2Z19leHBfYWxsKQpsX3NwJHNwID0gdW5saXN0KGxhcHBseShzdHJzcGxpdChsX3NwJGNsLCAiLi4iLCBmaXhlZCA9IFQpLCBmdW5jdGlvbih4KSB4WzFdKSkKbF9zcCRjbCA9IHVubGlzdChsYXBwbHkoc3Ryc3BsaXQobF9zcCRjbCwgIi4uIiwgZml4ZWQgPSBUKSwgZnVuY3Rpb24oeCkgeFsyXSkpCgpkZl9uYW1lcyA9IGxfc3BbbF9zcCRzcD09ImF4byIgfCAobF9zcCRYMjwoLTQpICYgbF9zcCRzcD09Im1vdSIpIHwgKGxfc3AkWDI+MC41ICYgbF9zcCRzcD09Im1vdSIpLF0KZ2dwbG90KGxfc3AsIGFlcyh4ID0gWDEsIHkgPSBYMiwgY29sb3VyID0gc3ApKSsKICBnZW9tX3BvaW50KCkrCiAgZ2dyZXBlbDo6Z2VvbV90ZXh0X3JlcGVsKGRhdGEgPSBkZl9uYW1lcywgbWFwcGluZyA9IGFlcyh4ID0gWDEsIHkgPSBYMiwgbGFiZWwgPSBjbCksIG1heC5vdmVybGFwcyA9IDEwMCkrCiAgdGhlbWVfY2xhc3NpYygpCmBgYAoKTmV0d29yayBpbiBNRFMgZnJvbSBjb3JyZWxhdGlvbnMKCmBgYHtyfQpjb3JfdGFiX2wgPSBsaXN0KCkKZm9yKG4gaW4gbmFtZXMoc3BfY29yX2wpKXsKICBkYXQgPSByZXNoYXBlMjo6bWVsdChzcF9jb3JfbFtbbl1dKQogIGRhdCRzcDEgPSBzdHJzcGxpdChzdHJzcGxpdChuLCAiLiIsIGZpeGVkID0gVClbWzFdXVsxXSwgIl8iLCBmaXhlZCA9IFQpW1sxXV1bMl0KICBkYXQkc3AyID0gc3Ryc3BsaXQoc3Ryc3BsaXQobiwgIi4iLCBmaXhlZCA9IFQpW1sxXV1bMl0sICJfIiwgZml4ZWQgPSBUKVtbMV1dWzJdCiAgCiAgZGF0JFZhcjEgPSBwYXN0ZTAoZGF0JHNwMSwgIl8iLCBkYXQkVmFyMSkKICBkYXQkVmFyMiA9IHBhc3RlMChkYXQkc3AyLCAiXyIsIGRhdCRWYXIyKQogIGNvcl90YWJfbFtbbl1dID0gZGF0Cn0KY29yX3RhYiA9IFJlZHVjZShyYmluZCwgY29yX3RhYl9sKQpjb3Jfc3AgPSByZXNoYXBlMjo6ZGNhc3QoY29yX3RhYiwgVmFyMSB+IFZhcjIsIHZhbHVlLnZhciA9ICJ2YWx1ZSIpCnJvd25hbWVzKGNvcl9zcCkgPSBjb3Jfc3BbLDFdCmNvcl9zcCA9IGNvcl9zcFssLTFdCmFkal9tYXQgPSBjb3Jfc3A+PTAuMwoKIyBidWlsZCBncmFwaCwgcHJvamVjdCB3aXRoIE1EUwpuZXR3b3JrX3NwID0gZ3JhcGhfZnJvbV9hZGphY2VuY3lfbWF0cml4KGFkal9tYXQsIHdlaWdodGVkPVQsIG1vZGU9InVuZGlyZWN0ZWQiLCBkaWFnPUYpCmxfc3AgPSBpZ3JhcGg6OmxheW91dF93aXRoX21kcyhuZXR3b3JrX3NwKQpsX3NwID0gZGF0YS5mcmFtZShsX3NwKQpsX3NwJGNsID0gY29sbmFtZXMoYWRqX21hdCkKcm93bmFtZXMobF9zcCkgPSBjb2xuYW1lcyhhZGpfbWF0KQpsX3NwJHNwID0gdW5saXN0KGxhcHBseShzdHJzcGxpdChsX3NwJGNsLCAiXyIpLCBmdW5jdGlvbih4KSB4WzFdKSkKbF9zcCRjbCA9IHVubGlzdChsYXBwbHkoc3Ryc3BsaXQobF9zcCRjbCwgIl8iKSwgZnVuY3Rpb24oeCkgeFsyXSkpCgpkZl9uYW1lcyA9IGxfc3BbbF9zcCRzcD09ImF4byIsXQpnZ3Bsb3QobF9zcCwgYWVzKHggPSBYMSwgeSA9IFgyLCBjb2xvdXIgPSBzcCkpKwogIGdlb21fcG9pbnQoKSsKICBnZ3JlcGVsOjpnZW9tX3RleHRfcmVwZWwoZGF0YSA9IGRmX25hbWVzLCBtYXBwaW5nID0gYWVzKHggPSBYMSwgeSA9IFgyLCBsYWJlbCA9IGNsKSwgbWF4Lm92ZXJsYXBzID0gMTAwKSsKICB0aGVtZV9jbGFzc2ljKCkKYGBgCgoKCmBgYHtyfQpjb21tb25fZ2VuZXMgPSBSZWR1Y2UoaW50ZXJzZWN0LCB0b3BfbWtfbGlzdCkKYW55X2dlbmVzID0gUmVkdWNlKHVuaW9uLCB0b3BfbWtfbGlzdCkKbm9uX3NoYXJlZF9nZW5lcyA9IGFueV9nZW5lc1shKGFueV9nZW5lcyAlaW4lIGNvbW1vbl9nZW5lcyldCm5vbl9zaGFyZWRfZ2VuZXMgPSBub25fc2hhcmVkX2dlbmVzW25vbl9zaGFyZWRfZ2VuZXMgJWluJSBSZWR1Y2UoaW50ZXJzZWN0LCBsYXBwbHkoYXZnX2V4cCwgcm93bmFtZXMpKV0KYXZnX2V4cF9zdWIgPSBsYXBwbHkoYXZnX2V4cCwgZnVuY3Rpb24oeCkgdChhcHBseSh4W25vbl9zaGFyZWRfZ2VuZXMsXSwgMSwgZnVuY3Rpb24oeSkgeS9tZWFuKHkpKSkpCmxhcHBseShhdmdfZXhwX3N1YiwgZGltKQoKZm9yKG4gaW4gbmFtZXMoYXZnX2V4cF9zdWIpKXsKICBzcCA9IHN0cnNwbGl0KG4sICJfIilbWzFdXVsyXQogIGNvbG5hbWVzKGF2Z19leHBfc3ViW1tuXV0pID0gcGFzdGUwKHNwLCAiLi4iLCBjb2xuYW1lcyhhdmdfZXhwX3N1Yltbbl1dKSkKfQphdmdfZXhwX2FsbCA9IHQoUmVkdWNlKGNiaW5kLCBhdmdfZXhwX3N1YikpCnNldC5zZWVkKDI5NTQpCmwgPSB1d290Ojp1bWFwKGF2Z19leHBfYWxsLCBtZXRyaWMgPSAiY29zaW5lIiwgcmV0X25uID0gVCwgbl9lcG9jaHMgPSAxMDAwKQpsX3NwID0gZGF0YS5mcmFtZShsJGVtYmVkZGluZykKcm93bmFtZXMobF9zcCkgPSByb3duYW1lcyhhdmdfZXhwX2FsbCkKbF9zcCRjbCA9IHJvd25hbWVzKGF2Z19leHBfYWxsKQpsX3NwJHNwID0gdW5saXN0KGxhcHBseShzdHJzcGxpdChsX3NwJGNsLCAiLi4iLCBmaXhlZCA9IFQpLCBmdW5jdGlvbih4KSB4WzFdKSkKbF9zcCRjbCA9IHVubGlzdChsYXBwbHkoc3Ryc3BsaXQobF9zcCRjbCwgIi4uIiwgZml4ZWQgPSBUKSwgZnVuY3Rpb24oeCkgeFsyXSkpCgpkZl9uYW1lcyA9IGxfc3BbbF9zcCRzcD09ImF4byIsXQpnZ3Bsb3QobF9zcCwgYWVzKHggPSBYMSwgeSA9IFgyLCBjb2xvdXIgPSBzcCkpKwogIGdlb21fcG9pbnQoKSsKICBnZ3JlcGVsOjpnZW9tX3RleHRfcmVwZWwoZGF0YSA9IGRmX25hbWVzLCBtYXBwaW5nID0gYWVzKHggPSBYMSwgeSA9IFgyLCBsYWJlbCA9IGNsKSwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgIG1heC5vdmVybGFwcyA9IDEwMCwgc2l6ZSA9IDMpKwogIHRoZW1lX2NsYXNzaWMoKQpgYGAKCg==